package cn.iocoder.yudao.module.trade.service.order.handler;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.RandomUtil;
import cn.iocoder.yudao.framework.common.enums.trade.TradeOrderStatusEnum;
import cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil;
import cn.iocoder.yudao.module.liveprice.api.LivePriceApi;
import cn.iocoder.yudao.module.member.api.address.MemberAddressApi;
import cn.iocoder.yudao.module.member.api.address.dto.MemberAddressRespDTO;
import cn.iocoder.yudao.module.member.api.config.dto.MemberConfMaterialDTO;
import cn.iocoder.yudao.module.member.api.config.dto.MemberConfRespDTO;
import cn.iocoder.yudao.module.pay.api.order.PayOrderApi;
import cn.iocoder.yudao.module.pay.api.order.dto.PayOrderCreateReqDTO;
import cn.iocoder.yudao.module.pay.enums.notify.PayNotifyTypeEnum;
import cn.iocoder.yudao.module.trade.controller.app.order.vo.AppTradeOrderOrderCreateReqVO;
import cn.iocoder.yudao.module.trade.controller.app.order.vo.AppTradeOrderOrderReqVO;
import cn.iocoder.yudao.module.trade.convert.order.TradeOrderConvert;
import cn.iocoder.yudao.module.trade.dal.dataobject.delivery.DeliveryPickUpStoreDO;
import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO;
import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDeliveryDO;
import cn.iocoder.yudao.module.trade.enums.delivery.DeliveryTypeEnum;
import cn.iocoder.yudao.module.trade.service.delivery.DeliveryPickUpStoreService;
import cn.iocoder.yudao.module.trade.service.order.TradeOrderDeliveryService;
import cn.iocoder.yudao.module.trade.service.order.TradeOrderQueryService;
import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateReqBO;
import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateRespBO;
import org.springframework.beans.factory.annotation.Autowired;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.Objects;

import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.addTime;
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
import static cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants.*;

/**
 * 交易模式处理器抽象基类
 */
public abstract class AbstractTransactionModeHandler implements TransactionModeHandler {
    
    @Autowired
    protected MemberAddressApi addressApi;
    @Autowired
    protected DeliveryPickUpStoreService pickUpStoreService;
    @Autowired
    protected TradeOrderDeliveryService orderDeliveryService;
    @Autowired
    protected PayOrderApi payOrderApi;
    @Autowired
    protected LivePriceApi livePriceApi;
    @Autowired
    protected TradeOrderQueryService orderQueryService;


    
    @Override
    public MemberConfMaterialDTO validateAndGetMaterialConf(MemberConfRespDTO memberConf, Integer metalType, Integer transactionType) {
        // 校验配置存在
        if (CollUtil.isEmpty(memberConf.getConfMaterials())) {
            throw ServiceExceptionUtil.exception(ORDER_CREATE_FAIL_USER_METAL_CONF_NOT_FOUND);
        }
        
        // 查找匹配的金属配置
        MemberConfMaterialDTO matchedMaterial = null;
        for (MemberConfMaterialDTO material : memberConf.getConfMaterials()) {
            if (material.getMetalType().equals(metalType) && material.getPolicyType().equals(transactionType)) {
                matchedMaterial = material;
                break;
            }
        }
        
        // 校验配置存在
        if (matchedMaterial == null) {
            throw ServiceExceptionUtil.exception(ORDER_CREATE_FAIL_USER_METAL_CONF_NOT_FOUND);
        }
        
        return matchedMaterial;
    }
    
    @Override
    public void validateTradeLimits(MemberConfMaterialDTO materialConf, AppTradeOrderOrderReqVO settlementReqVO) {
        BigDecimal weight = settlementReqVO.getWeight();
        if (weight == null) {
            return;
        }
        
        // 校验单笔重量限制
        if (materialConf.getWeightSingleLimit() != null && materialConf.getWeightSingleLimit().compareTo(BigDecimal.ZERO) > 0 &&
            weight.compareTo(materialConf.getWeightSingleLimit()) > 0) {
            throw ServiceExceptionUtil.exception(ORDER_CREATE_FAIL_WEIGHT_EXCEED_SINGLE_LIMIT);
        }
        // 累计限制(获取累计数值）
        BigDecimal weightSum = orderQueryService.queryWeightSumByTypeAndUser(materialConf.getPolicyType(), getLoginUserId());
        if (materialConf.getWeightTotalLimit() != null && materialConf.getWeightTotalLimit().compareTo(BigDecimal.ZERO) > 0 &&
                weightSum.add(weightSum).compareTo(materialConf.getWeightTotalLimit()) > 0) {
            throw ServiceExceptionUtil.exception(ORDER_CREATE_FAIL_WEIGHT_EXCEED_SINGLE_LIMIT);
        }

        // 校验小额下单限制
        if (Boolean.FALSE.equals(materialConf.getAllowSmallOrder()) && 
            weight.compareTo(BigDecimal.TEN) < 0) {
            throw ServiceExceptionUtil.exception(ORDER_CREATE_FAIL_SMALL_ORDER_NOT_ALLOWED);
        }


    }
    
    @Override
    public TradePriceCalculateRespBO calculatePrice(TradePriceCalculateReqBO calculateReqBO) {
        // 创建计算结果对象
        TradePriceCalculateRespBO result = new TradePriceCalculateRespBO();
        TradePriceCalculateRespBO calContext = new TradePriceCalculateRespBO();

        // 附加费
        BigDecimal  additionalPrice = calculateAdditionalPrice(calculateReqBO);
        // 工费
        BigDecimal  producePrice = calculateProducePrice(calculateReqBO.getExchangeWeight(), calculateReqBO.getMetalConf().getProcessingFee(), BigDecimal.ZERO);
        // 净金价
        BigDecimal  metalPrice = calculateMetalPrice(calculateReqBO);
        // 定金
        BigDecimal livePrice = livePriceApi.getConfPrice(calculateReqBO.getMetalType(), true);
        BigDecimal deposit = calculateDeposit(calculateReqBO.getWeight(), calculateReqBO.getMetalConf().getDepositWeight(),
                calculateReqBO.getLivePrice(), livePrice, BigDecimal.ZERO);

        // 计算总价
//        BigDecimal totalPrice = BigDecimal.ZERO
//                .add(additionalPrice)
//                .add(producePrice)
//                .add(metalPrice)
//                .add(deposit)
//                ;


        // 7. 设置计算结果
        calContext.setDeposit(deposit);
        calContext.setTotalPrice(BigDecimal.ZERO);
        calContext.setMetalPrice(metalPrice);
        calContext.setLivePrice(calculateReqBO.getLivePrice());
        calContext.setProducePrice(producePrice);
        calContext.setAdditionalPrice(additionalPrice);
//        price.setDiscountPrice();
//        price.setDeliveryPrice();
        // 计算下单应付价格
        BigDecimal payPrice = calculatePayPrice(calculateReqBO, calContext);
        result.setPayPrice(payPrice);

        result.setMetalPrice(calContext.getMetalPrice());
        setPriceResult(calculateReqBO, result, calContext);

        // 10. 校验最终价格
        if (result.getPayPrice().compareTo(BigDecimal.ZERO) < 0) {
            throw ServiceExceptionUtil.exception(PRICE_CALCULATE_PAY_PRICE_ILLEGAL);
        }
        
        return result;
    }

    protected abstract void setPriceResult(TradePriceCalculateReqBO calculateReqBO, TradePriceCalculateRespBO calContext, TradePriceCalculateRespBO context);

    @Override
    public TradeOrderStatusEnum getNextStatusBySuceess(TradeOrderDO order, Integer payType, Boolean isStore) {
        return null;
    }

    @Override
    public void setupDeliveryInfo(TradeOrderDO order, Long userId, AppTradeOrderOrderCreateReqVO createReqVO) {
        order.setDeliveryType(createReqVO.getDeliveryType());

        // 快递配送
        if (Objects.equals(createReqVO.getDeliveryType(), DeliveryTypeEnum.EXPRESS.getType())) {
            createMemberAddr(userId, createReqVO.getAddressId(), order.getOrderNo(), createReqVO.getDeliveryType());
        }
        else if (Objects.equals(createReqVO.getDeliveryType(), DeliveryTypeEnum.SHOP_DELIVERY.getType())) {
            createMemberAddr(userId, createReqVO.getAddressId(), order.getOrderNo(), createReqVO.getDeliveryType());
        }
        // 自提
        else if (Objects.equals(createReqVO.getDeliveryType(), DeliveryTypeEnum.PICK_UP.getType())) {
            createStoreAddr(order, createReqVO.getPickUpStoreId(), order.getOrderNo(), DeliveryTypeEnum.PICK_UP, createReqVO.getPickupInfoId());
        }
    }


    /**
     * 计算加工费
     */
    protected BigDecimal calculateProducePrice(BigDecimal weight, BigDecimal processFee, BigDecimal paidProcessFee) {
        if (weight == null || weight.compareTo(BigDecimal.ZERO) == 0 ||
                processFee == null || processFee.compareTo(BigDecimal.ZERO) == 0) {
            return BigDecimal.ZERO;
        }
        BigDecimal res = weight.multiply(processFee).subtract(paidProcessFee).setScale(2, RoundingMode.HALF_UP);
        if (res.compareTo(BigDecimal.ZERO) <=0) {
            return BigDecimal.ZERO;
        }
        return res;
    }

    /**
     * 计算额外费用
     */
    protected BigDecimal calculateAdditionalPrice(TradePriceCalculateReqBO calculateReqBO) {
        return BigDecimal.ZERO;
    }

    /**
     * 计算支付价格
     */
    protected BigDecimal calculatePayPrice(TradePriceCalculateReqBO calculateReqBO, TradePriceCalculateRespBO calContext) {
        return calContext.getMetalPrice();
    }

    /**
     * 计算定金
     * = 基础定金 + (当前价格-下单价格) * weight * 定金参数 / 10 - 已交定金
     */
    protected BigDecimal calculateDeposit(BigDecimal weight, BigDecimal weightConf, BigDecimal orderPrice, BigDecimal livePrice, BigDecimal paidDeposit) {
        if (weight == null || weight.compareTo(BigDecimal.ZERO) == 0 ||
                weightConf == null || weightConf.compareTo(BigDecimal.ZERO) == 0) {
            return BigDecimal.ZERO;
        }
        BigDecimal res = weight.multiply(weightConf)
                .add(weight.multiply(livePrice.subtract(orderPrice)).multiply(weightConf).divide(BigDecimal.TEN, RoundingMode.HALF_UP))
                .subtract(paidDeposit)
                .setScale(2, RoundingMode.HALF_UP);
        if (res.compareTo(BigDecimal.ZERO) <= 0) {
            return BigDecimal.ZERO;
        }
        return res;
    }
    /**
     * 计算净金价
     */
    protected BigDecimal calculateMetalPrice(TradePriceCalculateReqBO calculateReqBO) {
        if (calculateReqBO.getWeight() == null) {
            return BigDecimal.ZERO;
        }
        return calculateReqBO.getWeight().multiply(calculateReqBO.getLivePrice());
    }
    /**
     * 计算延期费
     */
    protected BigDecimal calculateDelayPrice(LocalDateTime orderTime, Integer freeDays, BigDecimal paidDelayFee, BigDecimal delayConf, BigDecimal weight) {
        Duration duration = LocalDateTimeUtil.between(orderTime, LocalDateTime.now());
        long delayDays = duration.toDays() - freeDays;
        if (delayDays <= 0) {
            return BigDecimal.ZERO;
        }
        BigDecimal delayFee = delayConf
                .multiply(weight.divide(BigDecimal.valueOf(1000), RoundingMode.HALF_UP))        // 配置的为kg/天费用
                .multiply(BigDecimal.valueOf(delayDays))
                .subtract(paidDelayFee)
                .setScale(2, RoundingMode.HALF_UP);
        if (delayFee.compareTo(BigDecimal.ZERO) <= 0) {
            return BigDecimal.ZERO;
        }
        return delayFee;
    }

    protected PayOrderCreateReqDTO initPayOrderCreate (String orderNo) {
        PayOrderCreateReqDTO payOrder = new PayOrderCreateReqDTO();
        payOrder.setPayType(PayNotifyTypeEnum.SETTLE_ORDER.getType());
        payOrder.setExpireTime(addTime((Duration.ofDays(1L))));
        payOrder.setOrderNo(orderNo);
        return payOrder;
    }

    protected void createMemberAddr(Long userId, Long addressId, String orderNo, Integer deliveryType) {
        MemberAddressRespDTO address = addressApi.getAddress(addressId, userId);
        Assert.notNull(address, "地址({}) 不能为空", addressId);
        TradeOrderDeliveryDO deliveryDO = TradeOrderConvert.INSTANCE.convertDelivery(address, orderNo);
        deliveryDO.setType(deliveryType);
        deliveryDO.setId(null);
        orderDeliveryService.insert(deliveryDO);
    }

    protected void createStoreAddr(TradeOrderDO order, Long addressId, String orderNo, DeliveryTypeEnum typeEnum, Long pickUpInfoId) {
        DeliveryPickUpStoreDO storeAddress = pickUpStoreService.getDeliveryPickUpStore(addressId);
        TradeOrderDeliveryDO deliveryDO = TradeOrderConvert.INSTANCE.convertDelivery(storeAddress, orderNo);
        deliveryDO.setPickupInfoId(pickUpInfoId);
        deliveryDO.setType(typeEnum.getType());
        deliveryDO.setId(null);
        order.setPickUpVerifyCode(RandomUtil.randomNumbers(8)); // 随机一个核销码，长度为 8 位
        orderDeliveryService.insert(deliveryDO);
    }
}